#!/usr/bin/python
import re
from serial import serialwin32 as serial
import sys
import threading
import datetime
import time as _time

from xml.etree import ElementTree
from mytimelib import LocalTz, UTCTz

SEGSIZE = 20

class ImGPS(object):
    def __init__(self, outfile):
        self.outfile = outfile
        self.errline = []
        self.GGA = re.compile('\$GPGGA,(?P<time>-?[0-9.]*),(?P<lat>-?[0-9.]*),\
(?P<latNS>[NS]),(?P<lon>-?[0-9.]*),(?P<lonEW>[EW]),\
(?P<qua>-?[0-9.]*),(?P<numsat>-?[0-9.]*),\
(?P<hordilu>-?[0-9.]*),(?P<alt>-?[0-9.]*),\
(?P<altunit>\w*),(?P<altgeo>-?[0-9.]*),\
(?P<altgeounit>\w*),(?P<age>[0-9.]*),\
\*(?P<lastchk>[0-9a-zA-Z.]*)'.replace('\n',''))
        self.GSA = re.compile('\$GPGSA,(?P<SelMode>\w*),(?P<Mode>\w*),\
(?P<satList>([0-9.]*,){12,12})(?P<PDOP>-?[0-9.]*),\
(?P<HDOP>-?[0-9.]*),(?P<VDOP>-?[0-9.]*)\
\*(?P<lastchk>[0-9a-zA-Z.]*)'.replace('\n',''))
        self.GSV = re.compile('\$GPGSV,(?P<totMsg>[0-9]+),(?P<curMsg>[0-9]+),\
(?P<satInView>[0-9.]*)\
(?P<satSigs>((,[0-9]*){4,4})+)\
\*(?P<lastchk>[0-9a-zA-Z.]*)'.replace('\n',''))
        self.GSVIndex = 0
        self.RMC = re.compile('\$GPRMC,(?P<time>-?[0-9.]*),(?P<navStatus>\w*),\
(?P<lat>-?[0-9.]*),(?P<latNS>[NS]),\
(?P<lon>-?[0-9.]*),(?P<lonEW>[EW]),\
(?P<speedNPH>-?[0-9.]*),(?P<RMCTrackDeg>-?[0-9.]*),\
(?P<date>-?[0-9.]*),(?P<RMCMagDeg>-?[0-9.]*),\
(?P<RMCMagEW>[EW]*),(?P<RMCFAAMode>\w*)\
\*(?P<lastchk>[0-9a-zA-Z.]*)'.replace('\n',''))
        self.VTG = re.compile('\$GPVTG,(?P<VTGDeg>-?[0-9.]*),(?P<VTGTF>\w*),\
(?P<VTGMDeg>-?[0-9.]*),(?P<VTGM>\w*),\
(?P<VTGSpeedKnots>-?[0-9.]*),(?P<VTGKnot>\w*),\
(?P<VTGSpeedKmPH>-?[0-9.]*),(?P<VTGKm>\w*),\
(?P<VTGFAAMode>\w*)\
\*(?P<lastchk>[0-9a-zA-Z.]*)'.replace('\n',''))
        self.dumpdata = []
    def __str__(self):
        rt =  '['
        for k in dir(self):
            if not k.startswith('_'):
                if k=='time':
                    rt += "'"+k+"':"+str(self.__getattribute__(k).astimezone(LocalTz()))+", "
                elif not isinstance(self.__getattribute__(k), type(self.__str__)) and not isinstance(self.__getattribute__(k), type(self.GGA)):
                    rt += "'"+k+"':"+str(self.__getattribute__(k))+", "
        return rt+ ']'
    def view(self):
        print '================================================'
        print 'Track Degree: %s(RMC), %s(VTG)'%(self.RMCTrackDeg, self.VTGDeg)
        print 'Speed: %3.4f m/s, %s km/h, %s knots'%(self.SpeedMPS,self.VTGSpeedKmPH,self.VTGSpeedKnots)
        print 'date: %s'%str(self.date)
        print 'Pos: %f,   %f'%(self.lat,self.lon)
        print 'Altitude: %3.4f m = %3.4f + %3.4f'%(self.alt+self.altgeo,self.alt,self.altgeo)        
        if hasattr(self, 'satSigs'):
            print 'Satelites -----------'
            for st in self.satSigs.keys():
                print st, self.satSigs[st]
        print '================================================'
        self.dumpdata.append((self.date,float(self.VTGDeg), self.SpeedMPS,
                               self.lon,self.lat, 
                              self.alt+self.altgeo))
        if len(self.dumpdata)%SEGSIZE == SEGSIZE-3:
            self.convert_kml()
        print len(self.dumpdata)
    def convert_kml(self, outfile = None):
        root = ElementTree.Element('kml')
        root.attrib['xmlns'] = 'http://earth.google.com/kml/2.1'
        docu = ElementTree.Element('Document')
        root.append(docu)
        segments = range(0, len(self.dumpdata), SEGSIZE)
        for ind in segments:
            place = ElementTree.Element('Placemark')
            
            el = ElementTree.Element('name')
            el.text= 'seg'+str(ind)
            place.append(el)
            
            el = ElementTree.Element('TimeSpan')
            bgtag = ElementTree.SubElement(el,'begin')
            bgtag.text= '%s' % str(self.dumpdata[ind][0]).replace(' ', 'T')
            if ind+SEGSIZE > len(self.dumpdata):
                eind = len(self.dumpdata)-1
            else:
                eind = ind+SEGSIZE-1
            edtag = ElementTree.SubElement(el,'end')
            edtag.text = '%s' % str(self.dumpdata[eind][0]).replace(' ', 'T')
            place.append(el)

            el = ElementTree.Element('LineString')
            tes = ElementTree.SubElement(el, 'tessellate')
            tes.text='1'
            cord = ElementTree.SubElement(el, 'coordinates')
            cord.text = ""
            ii = ind
            while ii < ind+SEGSIZE and ii < len(self.dumpdata):
                cord.text += "%f,%f,%f\n" %(self.dumpdata[ii][3],self.dumpdata[ii][4],self.dumpdata[ii][5])
                ii +=1
            place.append(el)
            
            docu.append(place)
        if not outfile:
            outfile = open('Realtime GPS.kml','w')
        eltree = ElementTree.ElementTree(root)
        eltree.write(outfile)
        outfile.close()
    def UTCtime(self, tm):
        td = datetime.datetime.utcnow()
        if tm.find('.')!=-1:
            tm = tm[:tm.find('.')]
        tm = datetime.time(tzinfo=UTCTz(),*_time.strptime(tm,'%H%M%S')[3:6])
        return td.combine(td,tm)
    def UTCdate(self, tm,dt):
        td = datetime.datetime.utcnow()
        if tm.find('.')!=-1:
            tm = tm[:tm.find('.')]
        tm = datetime.time(tzinfo=UTCTz(),*_time.strptime(tm,'%H%M%S')[3:6])
        td = td.combine(td,tm)
        td = datetime.datetime(tzinfo=UTCTz(),*_time.strptime(dt,'%d%m%y')[0:3])
        return td.combine(td,tm)
    def parse(self, line):
        if not line.startswith('$'):
            return
        if line.startswith('$GPGGA'):
            mat = self.GGA.search(line)
            if mat:
                pda = mat.groupdict()
                for k in pda.keys():
                    self.__setattr__(k,pda[k])
                self.time = self.UTCtime(self.time)
                self.lat = float(self.lat)/100.0
                if self.latNS.startswith('S'):
                    self.lat = -self.lat
                self.lon = float(self.lon)/100.0
                if self.lonEW.startswith('W'):
                    self.lon = -self.lon
                self.alt = float(self.alt)
                self.altgeo = float(self.altgeo)                
                self.timecorr()
            else:
                self.logerr(line)
        elif line.startswith('$GPGSA'):
            mat = self.GSA.search(line)
            if mat:
                pda = mat.groupdict()
                for k in pda.keys():
                    self.__setattr__(k,pda[k])
                self.satList = self.satList.split(',')
                self.satSigs = dict()
            else:
                self.logerr(line)
        elif line.startswith('$GPGSV'):
            mat = self.GSV.search(line)
            if mat:
                pda = mat.groupdict()
                oldsigs = self.satSigs
                for k in pda.keys():
                    self.__setattr__(k,pda[k])
                sigs = self.satSigs[1:].split(',')
                if self.GSVIndex == 0:
                    self.satSigs = dict()
                else:
                    self.satSigs = oldsigs

                for i in range(0,len(sigs),4):
                    self.satSigs[sigs[i]] = {'elev':sigs[i+1],
                                             'azi':sigs[i+2],
                                             'SNR':sigs[i+3]}
                if self.totMsg == self.curMsg:
                    self.GSVIndex = 0
                else:
                    self.GSVIndex = self.curMsg
            else:
                self.logerr(line)
        elif line.startswith('$GPRMC'):
            mat = self.RMC.search(line)
            if mat:
                pda = mat.groupdict()
                for k in pda.keys():
                    self.__setattr__(k,pda[k])
                self.date = self.UTCdate(self.time, self.date)
                self.time = self.UTCtime(self.time)
                self.lat = float(self.lat)/100.0
                if self.latNS.startswith('S'):
                    self.lat = -self.lat
                self.lon = float(self.lon)/100.0
                if self.lonEW.startswith('W'):
                    self.lon = -self.lon
                self.timecorr()
            else:
                self.logerr(line)
        elif line.startswith('$GPVTG'):
            mat = self.VTG.search(line)
            if mat:
                pda = mat.groupdict()
                for k in pda.keys():
                    self.__setattr__(k,pda[k])
                self.SpeedMPS = float(self.VTGSpeedKnots)*0.514444444
                self.SpeedMPH = float(self.VTGSpeedKnots)*1852
                self.view()
            else:
                self.logerr(line)
        else:
            print 'other :' + line
    def logerr(self, line):
        self.errline.append(line)
        self.outfile.write(line)
        print 'error line: '+line
    def timecorr(self, tm=None):
        if not tm:
            if hasattr(self,'date'):
                tm = self.date
            else:
                tm = self.time
        tnow = datetime.datetime(tzinfo=UTCTz(), *datetime.datetime.utcnow().utctimetuple()[0:7])
        if tnow > tm:
            print 'local time - GPS time = ' + str(tnow-tm)
        else:
            print 'GPS time - local time = ' + str(tm-tnow)

g = ImGPS(None)
k = """$GPGGA,153321.000,4000.6856,N,11619.3117,E,1,6,1.22,78.3,M,-6.5,M,,*7D


$GPRMC,153321.000,A,4000.6856,N,11619.3117,E,0.36,10.02,230407,,,A*5E


$GPVTG,10.02,T,,M,0.36,N,0.66,K,A*0B



$GPGGA,153322.000,4000.6857,N,11619.3118,E,1,6,1.22,78.3,M,-6.5,M,,*70


$GPGSA,A,3,03,01,31,19,16,13,,,,,,,1.52,1.22,0.90*01


$GPGSV,3,1,10,03,64,182,44,16,55,047,17,23,52,252,22,13,41,299,18*7D


$GPGSV,3,2,10,19,35,194,44,31,21,119,37,21,14,064,,01,11,171,41*70


$GPGSV,3,3,10,27,08,307,,24,01,029,15*79


$GPRMC,153322.000,A,4000.6857,N,11619.3118,E,0.34,9.64,230407,,,A*69


$GPVTG,9.64,T,,M,0.34,N,0.64,K,A*33



$GPGGA,153323.000,4000.6858,N,11619.3118,E,1,6,1.33,78.3,M,-6.5,M,,*7E


$GPGSA,A,3,03,01,31,19,16,13,,,,,,,2.49,1.33,2.11*03


$GPGSV,3,1,10,03,64,182,44,16,55,047,16,23,52,252,22,13,41,299,18*7C


$GPGSV,3,2,10,19,35,194,44,31,21,119,37,21,14,064,,01,11,171,41*70


$GPGSV,3,3,10,27,08,307,,24,01,029,15*79


$GPRMC,153323.000,A,4000.6858,N,11619.3118,E,0.19,9.64,230407,,,A*68


$GPVTG,9.64,T,,M,0.19,N,0.35,K,A*38



$GPGGA,153324.000,4000.6859,N,11619.3118,E,1,7,1.15,78.3,M,-6.5,M,,*7D


$GPRMC,153324.000,A,4000.6859,N,11619.3118,E,0.43,7.79,230407,,,A*63


$GPVTG,7.79,T,,M,0.43,N,0.79,K,A*3D


$GPGGA,153325.000,4000.6860,N,11619.3118,E,1,7,1.27,78.3,M,-6.5,M,,*77


"""

class MyReading(threading.Thread):
    def __init__(self, ser, fileout=None):
        threading.Thread.__init__(self)
        self.ser = ser
        self.goon = True
        self.fileout = fileout
        self.gps = ImGPS(fileout)
        
    def stop(self):
        self.goon = False
        self.fileout.close()
    def run(self):
        while self.goon:
            kk = self.ser.readline()
            if self.fileout:
                self.fileout.write(kk)
            self.gps.parse(kk)

g_ser = serial.Serial(baudrate=9600,parity='N',rtscts=0)

def scan():
    """scan for available ports. return a list of tuples (num, name)"""
    available = []
    for i in range(256):
        try:
            g_ser.setPort(i)
            g_ser.open()
            print str(g_ser)
            available.append( (i, g_ser.portstr))
            while g_ser.isOpen():
                g_ser.close()   #explicit close 'cause of delayed GC in java
                _time.sleep(1)
                print 'try closing port %d '% i
        except serial.SerialException:
            pass
    return available

def main():
    serlist = scan()
    print "Found ports:"
    for n,s in serlist:
        print "(%d) %s" % (n,s)
    print 'Which do u want to connect: ',
    while True:
        try:
            portnum = int(raw_input())
            break
        except ValueError:
            print 'Which do u want to connect: ',
    try:
        fo = open('pos_serial.data','w+')
        while True:
            try:
                g_ser.setPort(portnum)
                ser = g_ser
                ser.open()
                break
            except:
                print 'try again in one second'
                ser.close()
                _time.sleep(1)
                pass
        th = MyReading(ser,fileout=fo)
        th.start()
        while True:
            print 'Enter message: '
            st = raw_input()
            if st.startswith('end'):
                break
#            ser.write(st)
#            ser.flush()
    except Exception,err:
        print err
#    finally Error, err:
        if 'th' in dir():
            th.stop()
        fo.close()
        if 'ser' in dir():
            ser.close()

if __name__=="__main__":
    main()
